package com.tc.quartz.server.core;

import com.tc.quartz.server.core.bean.JobRunLogBean;
import com.tc.quartz.server.core.bean.SystemDictBean;
import com.tc.quartz.util.QrtzUtil;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobExecutionContext;
import org.quartz.Trigger;
import org.quartz.impl.triggers.SimpleTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import javax.sql.rowset.serial.SerialBlob;
import java.sql.*;
import java.util.List;

/**
 * quartz日志管理，日志操作错误不应影响任务运行结果。
 * @author yongjin.ye
 * 
 */
public class PaymentQuartzServiceImpl implements IPaymentQuartzService {
	
	protected static Logger log = LoggerFactory.getLogger(PaymentQuartzServiceImpl.class);

	public static final String INSERT_JOB_RUN_LOG_INFO = "insert into {0}_job_run_log (fire_instance_name, sched_name, job_group, job_name, job_desc, job_class_name, job_data_map, trigger_type, plan_fire_time, next_fire_Time, run_start_time, run_end_time, run_used_time, result_code, result_msg, remark) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
	public static final String UPDATE_JOB_RUN_LOG_INFO = "update {0}_job_run_log set run_end_time=?, run_used_time=run_end_time-run_start_time, result_code=?, result_msg=?, remark=? where id=? ";
	public static final String SELECT_SYSTEM_DICT_INFO = "select t.id, t.dict_type as dictType, t.dict_key as dictKey, t.dict_value as dictValue, t.expand, t.remark from quartz_system_dict t where t.dict_type=? and t.dict_key=? limit 0, 1";

	/** 日志表名前缀 */
	private String tablePrefix = "QRTZ";
	private String remarkCharset = "GBK";
	private JdbcTemplate jdbcTemplate;

	/**
	 * 记录任务运行日志
	 * @param runLog
	 * @return
	 * @throws Exception
	 */
	@Override
	public void insertJobRunLogOfTriggerMisfired(Trigger trigger) throws Exception {
		String fireInstanceName = null;
		if (trigger instanceof SimpleTriggerImpl) {
			SimpleTriggerImpl simpleTrigger = (SimpleTriggerImpl) trigger;
			fireInstanceName = simpleTrigger.getFireInstanceId();
		}
		JobRunLogBean runlog = new JobRunLogBean();
		runlog.setFireInstanceName(fireInstanceName);
		runlog.setSchedName(null);
		runlog.setJobGroup(trigger.getJobKey().getGroup());
		runlog.setJobName(String.format("%s-%s-%s", trigger.getJobKey().getName(), System.currentTimeMillis(), QrtzUtil.genRandom(5)));
		runlog.setJobDesc(trigger.getDescription());
		runlog.setJobClassName(null);
		runlog.setJobDataMap(null);
		runlog.setTriggerType(null);
		runlog.setPlanFireTime(trigger.getStartTime().getTime());
		runlog.setNextFireTime(null == trigger.getNextFireTime() ? 0L : trigger.getNextFireTime().getTime());
		runlog.setRunStartTime(System.currentTimeMillis());
		runlog.setRunEndTime(0L);
		runlog.setRunUsedTime(0L);
		runlog.setResultCode(StatusEnum.ERR_TRIGGER_MISFIRED.getValue());
		runlog.setResultMsg(StatusEnum.ERR_TRIGGER_MISFIRED.getDesc());
		runlog.setRemark(String.format("MisfireInstruction=%s", trigger.getMisfireInstruction()));
		this.insertJobRunLog(runlog);
	}
	
	@Override
	public long insertJobRunLog(JobExecutionContext context, long startTime) throws Exception {
		return this.insertJobRunLog(context, startTime, false);
	}
	
	@Override
	public long insertJobRunLog(JobExecutionContext context, long startTime, boolean isRepeated) throws Exception {
		JobRunLogBean runlog = new JobRunLogBean();
		runlog.setFireInstanceName(context.getFireInstanceId());
		runlog.setSchedName(context.getScheduler().getSchedulerName());
		runlog.setJobGroup(context.getJobDetail().getKey().getGroup());
		runlog.setJobName(String.format("%s%s", context.getJobDetail().getKey().getName(), !isRepeated ? "": String.format("-%s-%s", System.currentTimeMillis(), QrtzUtil.genRandom(5))));
		runlog.setJobDesc(context.getJobDetail().getDescription());
		runlog.setJobClassName(context.getJobDetail().getJobClass().getName());
		runlog.setJobDataMap(QrtzUtil.obj2json(context.getJobDetail().getJobDataMap(), context.getTrigger().getJobDataMap()));
		runlog.setTriggerType("DEFAULT");
		runlog.setPlanFireTime(context.getScheduledFireTime().getTime());
		runlog.setNextFireTime(null == context.getNextFireTime() ? 0L : context.getNextFireTime().getTime());
		runlog.setRunStartTime(startTime);
		runlog.setRunEndTime(0L);
		runlog.setRunUsedTime(0L);
		runlog.setResultCode(isRepeated ? StatusEnum.ERR_REPEATED.getValue() : StatusEnum.RUNING.getValue());
		runlog.setResultMsg(isRepeated ? StatusEnum.ERR_REPEATED.getDesc() : StatusEnum.RUNING.getDesc());
		runlog.setRemark(null);
		return this.insertJobRunLog(runlog);
	}
	
	@Override
	public long insertJobRunLog(final JobRunLogBean runlog) throws Exception {
		final String sql = QrtzUtil.fmtMsg(INSERT_JOB_RUN_LOG_INFO, this.getTablePrefix());
		KeyHolder keyHolder = new GeneratedKeyHolder();
        PreparedStatementCreator creator = new PreparedStatementCreator() {
            @Override
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
                int index = 0;
	            ps.setString(++index, runlog.getFireInstanceName());//触发实例name
	   			ps.setString(++index, runlog.getSchedName());//调度器名称
	   			ps.setString(++index, runlog.getJobGroup());//任务所属组
	   			ps.setString(++index, runlog.getJobName());//任务名称（重复执行则增加随机数)
	   			ps.setString(++index, runlog.getJobDesc());//任务描述
	   			ps.setString(++index, runlog.getJobClassName());//job class
	   			ps.setString(++index, runlog.getJobDataMap());//JobDataMap
	   			ps.setString(++index, runlog.getTriggerType());//trigger_type
	   			ps.setLong(++index, runlog.getPlanFireTime());//计划本次触发时间
	   			ps.setLong(++index, runlog.getNextFireTime());//计划下次触发时间
	   			ps.setLong(++index, runlog.getRunStartTime());//执行开始时间
	   			ps.setLong(++index, runlog.getRunEndTime());
	   			ps.setLong(++index, runlog.getRunUsedTime());
	   			ps.setString(++index, runlog.getResultCode());
	   			ps.setString(++index, runlog.getResultMsg());
	   			ps.setString(++index, runlog.getRemark());
                return ps;
            }
        };
        jdbcTemplate.update(creator, keyHolder);
        Long generatedId = keyHolder.getKey().longValue();
		log.debug("记录定时任务日志后主键是：{} ", generatedId);
		return generatedId;
	}

	/** 
	 * 任务运行结束后，修改运行状态和运行时间等信息
	 * @param runLog
	 * @return
	 * @throws Exception 
	 * @throws Exception
	 */
	@Override
	public int updateJobRunLog(final long logId, final String resultCode, final String resultMsg, final String remark, final long endTime)  {
		int updateResult = 0;
		try {
			final Blob remarkBlob = StringUtils.isBlank(remark) ? null : new SerialBlob(remark.getBytes(this.getRemarkCharset()));
			final String sql = QrtzUtil.fmtMsg(UPDATE_JOB_RUN_LOG_INFO, this.getTablePrefix());
			PreparedStatementCreator creator = new PreparedStatementCreator() {
				@Override
                public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                    PreparedStatement ps = con.prepareStatement(sql);
                    int index = 0;
		            ps.setLong(++index, endTime);
		   			ps.setString(++index, QrtzUtil.substring(resultCode, 0, 10));
		   			ps.setString(++index, QrtzUtil.substring(resultMsg, 0, 165));
		   			ps.setBlob(++index, remarkBlob);
		   			ps.setLong(++index, logId);
                    return ps;
                }
            };
            updateResult = jdbcTemplate.update(creator);
			log.debug("更新定时任务日志结果：updateResult= " + updateResult);
		} catch (Exception e) {
			log.error("更新定时任务日志异常", e);
		} 
		return updateResult;
	}

	/**
	 * TRIGGER_NEXT_JOB=0000,0001|scheduler-cap-fo-1|DEFAULT|SettleCreateFundoutBatch|aaa:111,bb:2,cc:%userInfo%
	 */
	@Override
	public String triggerNextJob(JobExecutionContext context) throws Exception {
		try {
			List<?> urlList = this.getJdbcTemplate().queryForList(SELECT_SYSTEM_DICT_INFO, DICT_TYPE, OPEN_API_URL);
			List<?> tokenList = this.getJdbcTemplate().queryForList(SELECT_SYSTEM_DICT_INFO, DICT_TYPE, OPEN_API_TOKEN);
			SystemDictBean dictUrl = new SystemDictBean();
			SystemDictBean dictToken = new SystemDictBean();
			BeanUtils.copyProperties(dictUrl, urlList.get(0));
			BeanUtils.copyProperties(dictToken, tokenList.get(0));
			log.info("触发nextjob：dictUrl= " + dictUrl.getDictValue());
			String[] nextJob = context.getJobDetail().getJobDataMap().getString(TRIGGER_NEXT_JOB).split("\\|");
			String jobData[] = nextJob[4].split("\\,");
			StringBuffer triggerJobData = new StringBuffer("");
			for (int i = 0; i < jobData.length; i++) {
				String[] kv = jobData[i].split("\\:");
				String k = kv[0];
				String v = kv[1];
				if (v.startsWith("%") && v.endsWith("%")) {
					v = context.getTrigger().getJobDataMap().getString(v.replace("%", ""));
				}
				triggerJobData.append(String.format("%s=%s\n", k, v));
			}
			//key均为固定值
/*			NameValuePair nv1 = new BasicNameValuePair("dtlSchedName",nextJob[1]);
			NameValuePair nv2 = new BasicNameValuePair("dtlJobGroup",nextJob[2]);
			NameValuePair nv3 = new BasicNameValuePair("dtlJobName",nextJob[3]);
			NameValuePair nv4 = new BasicNameValuePair("triggerJobDataStr",triggerJobData.toString());
			//url=http://********:8080/payment-quartz
			String respBody = Request.Post(dictUrl.getDictValue() + "/jobManager/triggerJob.htm").connectTimeout(1000 * 60).socketTimeout(1000 * 60).version(HttpVersion.HTTP_1_1)
					.addHeader(TOKEN, dictToken.getDictValue()).bodyForm(nv1, nv2, nv3, nv4)
					.execute().returnContent().asString();
			log.info("触发nextjob返回结果：{}" + respBody);
			
			return respBody;*/
			return "";
		} catch (Exception e) {
			throw new Exception("触发nextjob异常", e);
		}
	}


	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	public String getTablePrefix() {
		return tablePrefix;
	}

	public void setTablePrefix(String tablePrefix) {
		this.tablePrefix = tablePrefix;
	}

	public String getRemarkCharset() {
		return remarkCharset;
	}

	public void setRemarkCharset(String remarkCharset) {
		this.remarkCharset = remarkCharset;
	}

}
